package com.example.f1.controller.tcc.impl;

import com.example.f1.controller.Cart;
import com.example.f1.controller.tcc.CartService;
import com.example.f1.domain_param.CartParam;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import lombok.extern.slf4j.Slf4j;
import org.example.dayu521.OrRes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static java.lang.StringTemplate.STR;

@RestController
@Slf4j
@RequestMapping("/tcc/cart")
public class CartServiceImpl implements CartService {

    public static final String rollback_pre="cart:rollback:";

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private Cart cart;

    @Override
    @PostMapping("/rob")
    public OrRes<?> rob( @BusinessActionContextParameter(paramName = "params") @RequestBody CartParam.Rob rob) {
        var res=cart.rob(rob);
        if(!res.is_ok()){
            return new OrRes<>(res.code(),res.info(),List.of());
        }
        return res;
    }

    //提交和回滚方法需要解决三个 tcc问题,幂等,悬挂,空回滚问题
    @Override
    public boolean rob_confirm( @BusinessActionContextParameter(paramName = "params") CartParam.Rob rob) {
        log.info("rob 确认");
        String key = Cart.cart_prefix + rob.login_id() + ":" + rob.settle_id();
        redisTemplate.delete(key);
        return true;
    }

    //如何分辨是幂等还是空回滚?
    //考虑到事务回滚概率不高,所以由回滚操作进行判断
    @Override
    public boolean rob_cancel( @BusinessActionContextParameter(paramName = "params") CartParam.Rob rob) {
        log.info("rob 回滚,参数:{}",rob);

        //减少竞争
        String rollback_key = rollback_pre + rob.login_id() + ":" + rob.settle_id();
        Boolean can_rollback = redisTemplate.opsForValue().setIfAbsent(rollback_key,
                "1", 5 * 60, TimeUnit.SECONDS);
        if(!can_rollback){
            log.info(STR."检测到正在回滚购物车 \{rob.login_id()}:\{rob.settle_id()},退出当前回滚");
            return true;
        }
        //检测空回滚,转化为竞争关系
        //主要方法是抢夺try的资源.即事先加锁,防止try的悬空
        var oc = redisTemplate.opsForValue().setIfAbsent(Cart.cart_prefix + rob.login_id()+":" + rob.settle_id(),
                "0", 30, TimeUnit.SECONDS);
        if(oc||"0".equals(redisTemplate.opsForValue().get(Cart.cart_prefix + rob.login_id()+":" + rob.settle_id()))){//并发修改时也会失败,所以判断修改后的结果
            log.warn("当前是空回滚,不需要操作.已设置状态,来防止可能的悬空try");
            return true;
        }

        OrRes<?> r=null;
        try {
            //本身幂等
            r=cart.reverse_rob(new CartParam.RobReverse(rob.login_id(), rob.settle_id()));
        }catch (Exception e){
            log.error("回滚出错,请检查redis一致性 => {}",e);
            redisTemplate.delete(rollback_key);
            return true;
        }
        if(!r.is_ok()){
            log.error(r.info());
            return true;
        }
        redisTemplate.delete(rollback_key);
        log.warn("rob_cancel => 回滚成功");
        return true;
    }
}
